package com.beiming.libra.service.impl;

import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.beiming.libra.common.Assert;
import com.beiming.libra.common.enums.ErrorCode;
import com.beiming.libra.common.enums.SMSEnum;
import com.beiming.libra.dao.DictionaryMapper;
import com.beiming.libra.domain.Dictionary;
import com.beiming.libra.redis.enums.RedisKey;
import com.beiming.libra.service.RedisService;
import com.beiming.libra.service.SMSService;
import com.smn.account.CloudAccount;
import com.smn.client.SmnClient;
import com.smn.common.HttpResponse;
import com.smn.model.request.sms.SmsPublishRequest;
import io.jsonwebtoken.lang.Collections;
import lombok.extern.slf4j.Slf4j;

/**
 *
 * 华为短信接口，详见： https://github.com/SimpleMessageNotification/smn-sdk-java/wiki
 * 
 * sdk错误码说明文档，详见：https://support.huaweicloud.com/api-smn/zh-cn_topic_0036017309.html
 * 
 * @author lb
 *
 */
@Service
@Slf4j
public class SMSServiceImpl implements SMSService, InitializingBean {

  private static final int SMS_RESPONSE_SUCCESS = 200;
  private static final String PARAM_PREFIX = "${";
  private static final String PARAM_SUFFIX = "}";

  @Value("${sms.sdk.accountUserName}")
  private String accountUserName;
  @Value("${sms.sdk.accountPassword}")
  private String accountPassword;
  @Value("${sms.sdk.accountDomainName}")
  private String accountDomainName;
  @Value("${sms.sdk.accountRegionId}")
  private String accountRegionId;

  /**
   * 短信签名,需要在消息通知服务的自助页面申请签名，申请办理时间约2天
   */
  @Value("${sms.sdk.accountSignId}")
  private String accountSignId;

  @Value("${sms.sendSwitch}")
  private boolean sendSwitch;
  @Value("${sms.sendFrequencySeconds}")
  private int sendFrequencySeconds;

  @Resource
  private DictionaryMapper dictionaryMapper;
  @Resource
  private RedisService redisService;

  private SmnClient smnClient;

  @Override
  public void send(String mobilePhone, SMSEnum smsEnum, Map<String, String> data) {
    // 后期应调整为异步方式
    Dictionary dic = dictionaryMapper.selectByCode(smsEnum.toString());
    if (dic == null) {
      log.error("sms template : [{}] not exists", smsEnum);
      return;
    }
    String template = dic.getValue();
    if (!Collections.isEmpty(data)) {
      for (Map.Entry<String, String> entry : data.entrySet()) {
        template = StringUtils.replace(template, PARAM_PREFIX + entry.getKey() + PARAM_SUFFIX,
            entry.getValue());
      }
    }

    log.info("ready to send sms, mobilePhone:{} , message: {}", mobilePhone, template);
    doSend(mobilePhone, template);
  }

  /**
   * 检查是否被限制发送，目前只检测发送频率
   * 
   * @param mobilePhone
   * @param message
   * @return
   */
  private boolean canSend(String mobilePhone, String message) {
    String res = redisService.get(RedisKey.SMS_FREQUENCY, mobilePhone + message);
    boolean canSend = StringUtils.isBlank(res);
    if (!canSend) {
      log.error("send frequency limit");
    }
    return canSend;
  }

  /**
   * 发送短信
   * 
   * @param phone 手机号，号码格式 (+)(国家码)(手机号码)
   * @param message 发送内容
   */
  private void doSend(String phone, String message) {
    if (!sendSwitch) {
      log.warn("sms switch is {}", sendSwitch);
      return;
    }
    if (StringUtils.containsAny(message, "[", "]", "【", "】")) {
      log.error("sms message contains illegal char");
      return;
    }

    if (!canSend(phone, message)) {
      return;
    }

    // 发送手机号码 号码格式 (+)(国家码)(手机号码)
    SmsPublishRequest smsPublishRequest = new SmsPublishRequest();

    // 设置手机号码
    smsPublishRequest.setEndpoint(phone);
    // 设置短信内容，短信内容中不要出现【】或者[]
    smsPublishRequest.setMessage(message);
    // 设置短信签名
    smsPublishRequest.setSignId(accountSignId);

    // 发送短信
    try {
      long start = System.currentTimeMillis();
      HttpResponse res = smnClient.smsPublish(smsPublishRequest);
      log.info("sms send use millis time : {}", System.currentTimeMillis() - start);
      Assert.isNotNull(res, ErrorCode.UNEXCEPTED, "sms sdk response is null");
      if (res.getHttpCode() == SMS_RESPONSE_SUCCESS) {
        log.info("send sms message success");
        redisService.set(RedisKey.SMS_FREQUENCY, phone + message, phone, sendFrequencySeconds,
            TimeUnit.SECONDS);
      } else {
        log.error("sms send faild response http code : {}", res.getHttpCode());
      }
    } catch (Exception e) {
      log.error("send sms message error :", e);
    }
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    initSMSClient();
  }

  private void initSMSClient() {
    CloudAccount cloudAccount =
        new CloudAccount(accountUserName, accountPassword, accountDomainName, accountRegionId);
    smnClient = cloudAccount.getSmnClient();
  }
}
